package com.lovezy.platform.together.service.impl;

import com.lovezy.platform.core.base.enums.StatusEnum;
import com.lovezy.platform.core.base.query.Pager;
import com.lovezy.platform.core.base.service.MapperService;
import com.lovezy.platform.core.valid.ValidatorUtil;
import com.lovezy.platform.shop.service.ShopManager;
import com.lovezy.platform.shop.service.ShopService;
import com.lovezy.platform.together.enums.ApplyStatus;
import com.lovezy.platform.together.exception.TogetherException;
import com.lovezy.platform.together.exception.TogetherExceptionEnum;
import com.lovezy.platform.together.form.CreateTogetherGameForm;
import com.lovezy.platform.together.form.DealApplyForm;
import com.lovezy.platform.together.form.JoinTogetherForm;
import com.lovezy.platform.together.form.QuitTogetherForm;
import com.lovezy.platform.together.mapper.TogetherMapper;
import com.lovezy.platform.together.model.Together;
import com.lovezy.platform.together.model.TogetherDetail;
import com.lovezy.platform.together.model.TogetherExample;
import com.lovezy.platform.together.service.TogetherDetailService;
import com.lovezy.platform.together.service.TogetherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Optional;

import static com.lovezy.platform.core.utils.ObjectUtil.copyTo;
import static com.lovezy.platform.core.utils.ObjectUtil.nonNull;

/**
 * 约局服务实现类
 * Created by jin on 2017/11/19.
 */
@Service
public class TogetherServiceImpl extends MapperService<Together,Integer,TogetherMapper> implements TogetherService {

    @Autowired
    private ShopService shopService;

    @Autowired
    private TogetherDetailService togetherDetailService;

    @Override
    public Together addNewTogetherGame(CreateTogetherGameForm f) {
        Together together = new Together();
        copyTo(f, together);
        together.setConfirmTotal(0);
        together.setModifyId(f.getCreateId());
        together.setStatus(StatusEnum.NORMAL.value());
        together.setApplyStatus(ApplyStatus.APPLYING.value());
        Date now = new Date();
        together.setGmtCreate(now);
        together.setGmtModify(now);
        mapper.insert(together);
        return together;
    }

    @Override
    public boolean isTogetherBelongToShop(Integer togetherId, Integer shopId) {
        Together together = mapper.selectByPrimaryKey(togetherId);
        if (together == null) {
            return false;
        }
        return together.getShopId().equals(shopId);
    }

    @Override
    public Optional<Together> findById(Integer togetherId) {
        TogetherExample example = new TogetherExample();
        example.createCriteria()
                .andTogetherIdEqualTo(togetherId)
                .andStatusEqualTo(StatusEnum.NORMAL.value());
        return Pager.doSingleton(() -> mapper.selectByExample(example));
    }

    @Override
    public void agreeTogetherGameApply(DealApplyForm form) {
        ValidatorUtil.validate(form);
        Integer shopId = form.getShopId();
        String mid = form.getMemberId();
        nonNull(shopId);
        dealWithApply(ApplyStatus.AGREE, form.getTogetherId(), getShopManager(shopId, mid));
    }

    @Override
    public void rejectTogetherGameApply(DealApplyForm form) {
        ValidatorUtil.validate(form);
        Integer shopId = form.getShopId();
        String mid = form.getMemberId();
        nonNull(shopId);
        dealWithApply(ApplyStatus.REJECT, form.getTogetherId(), getShopManager(shopId, mid));
    }

    @Override
    public void cancelTogetherGameApply(DealApplyForm form) {
        ValidatorUtil.validate(form);
        String mid = form.getMemberId();
        Integer togetherId = form.getTogetherId();
        Together together = findById(togetherId).orElseThrow(() -> new TogetherException(TogetherExceptionEnum.TOGETHER_NOT_FOUND));
        if (!together.getCreateId().equals(mid)) {
            throw new TogetherException(TogetherExceptionEnum.NO_AUTH_TO_HANDLE_TOGETHER_APPLY);
        }
        checkCanReview(together);

        Together updateTogether = new Together();
        updateTogether.setTogetherId(togetherId);
        updateTogether.setApplyStatus(ApplyStatus.CANCEL.value());
        updateTogether.setGmtModify(new Date());
        updateTogether.setModifyId(mid);
        mapper.updateByPrimaryKeySelective(updateTogether);
    }

    @Transactional
    @Override
    public void joinTogetherGame(JoinTogetherForm form) {
        ValidatorUtil.validate(form);
        Integer togetherId = form.getTogetherId();
        String memberId = form.getMemberId();
        Together together = findById(togetherId).orElseThrow(() -> new TogetherException(TogetherExceptionEnum.TOGETHER_NOT_FOUND));
        if (together.getApplyStatus() != ApplyStatus.AGREE.value()) {
            throw new TogetherException(TogetherExceptionEnum.CANT_JOIN_NOT_AGREE_TOGETHER);
        }
        if (togetherDetailService.hasExistInTogether(memberId, togetherId)) {
            throw new TogetherException(TogetherExceptionEnum.HAS_EXIST_IN_TOGETHER);
        }

        togetherDetailService.addOne(togetherId, memberId);
        Together update = new Together();
        update.setTogetherId(togetherId);
        update.setConfirmTotal(together.getConfirmTotal() + 1);
        update.setGmtModify(new Date());
        mapper.updateByPrimaryKeySelective(update);
    }

    @Override
    public void cancelJoinTogetherGame(QuitTogetherForm form) {
        ValidatorUtil.validate(form);
        Integer detailId = form.getTogetherDetailId();
        String memberId = form.getMemberId();
        TogetherDetail detail = togetherDetailService.findById(detailId).orElseThrow(() -> new TogetherException(TogetherExceptionEnum.TOGETHER_NOT_FOUND));
        Together together = findById(detail.getTogetherId()).orElseThrow(() -> new TogetherException(TogetherExceptionEnum.TOGETHER_NOT_FOUND));
        if (together.getApplyStatus() != ApplyStatus.AGREE.value()) {
            throw new TogetherException(TogetherExceptionEnum.CANT_JOIN_NOT_AGREE_TOGETHER);
        }

        togetherDetailService.removeOne(detailId, memberId);
        Together update = new Together();
        update.setTogetherId(together.getTogetherId());
        update.setConfirmTotal(together.getConfirmTotal() - 1);
        update.setGmtModify(new Date());
        mapper.updateByPrimaryKeySelective(update);
    }

    private void dealWithApply(ApplyStatus handle, Integer togetherId, ShopManager manager) {
        boolean isBelong = isTogetherBelongToShop(togetherId, manager.getShopId());
        if (!isBelong) {
            throw new TogetherException(TogetherExceptionEnum.NO_AUTH_TO_HANDLE_TOGETHER_APPLY);
        }
        Optional<Together> togetherOptional = findById(togetherId);
        if (!togetherOptional.isPresent()) {
            throw new TogetherException(TogetherExceptionEnum.TOGETHER_NOT_FOUND);
        }
        Together dbTogether = togetherOptional.get();
        checkCanReview(dbTogether);

        Together updateTogether = new Together();
        updateTogether.setTogetherId(togetherId);
        updateTogether.setApplyStatus(handle.value());
        updateTogether.setGmtModify(new Date());
        updateTogether.setModifyId(manager.getMemberId());
        mapper.updateByPrimaryKeySelective(updateTogether);
    }

    private void checkCanReview(Together together) {
        if (together.getApplyStatus() != ApplyStatus.APPLYING.value()) {
            throw new TogetherException(TogetherExceptionEnum.TOGETHER_IS_ALREADY_REVIEWED);
        }
    }


    private ShopManager getShopManager(Integer shopId,String mid) {
        return shopService.isShopManager(shopId, mid).orElseThrow(() -> new TogetherException(TogetherExceptionEnum.NO_AUTH_TO_HANDLE_TOGETHER));
    }

}
